﻿using System;
using System.Collections.Generic;
using System.Linq;

/*
 * Author : Daniel Mcliver
 */
// ReSharper disable CheckNamespace
namespace SuperListLibrary
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// A super set of the System.Collections.Generic.List&lt;T&gt; class, adding operator methods into the mix.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SuperList<T> : List<T>
    {
        /// <summary>
        /// Appends a list to another
        /// </summary>
        /// <returns></returns>
        public static SuperList<T> operator +(SuperList<T> l1,SuperList<T> l2)
        {
            SuperList<T> l = new SuperList<T>();
            l.AddRange(l1);
            l.AddRange(l2);
            return l;
        }

        /// <summary>
        /// Adds an item to the end of the list
        /// </summary>
        /// <param name="l1">The list </param>
        /// <param name="element">The item</param>
        /// <returns>The original list with the item appended to it</returns>
        public static SuperList<T> operator +(SuperList<T> l1, T element)
        {
            l1.Add(element);
            return l1;
        } 

        /// <summary>
        /// Shifts items to left n times i.e removes all n items at the beginning of the list
        /// </summary>
        /// <param name="l1">The original list</param>
        /// <param name="times">The number of times to shift the list</param>
        /// <returns>The resultant list</returns>
        public static SuperList<T> operator <<(SuperList<T> l1, int times)
        {
            return l1.Skip(times).ToSuperList();
        }

        /// <summary>
        /// Shifts items to right n times i.e removes all n items at the end of the list
        /// </summary>
        /// <param name="l1">The original list</param>
        /// <param name="times">The number of times to shift the list</param>
        /// <returns>The resultant list</returns>
        public static SuperList<T> operator >>(SuperList<T> l1, int times)
        {
            return l1.Take(l1.Count - times).ToSuperList();
        } 

        /// <summary>
        /// Duplicates the list n times and puts the duplicates in with the original values as one list
        /// </summary>
        /// <returns>The list containing n number of duplicates</returns>
        public static SuperList<T> operator *(SuperList<T> list, int times)
        {
            SuperList<T> l = new SuperList<T>();
            Enumerable.Range(1,times).ToList().ForEach(i => l.AddRange(list));
            
            return l;
        }

        /// <summary>
        /// Inserts a collection of elements into the list at the specified index.
        /// </summary>
        public void Insert(int index, SuperList<T> items)
        {
            if (items == null) throw new ArgumentNullException("items");

            items.Reverse();
            foreach (var item in items)
            {
                Insert(index,item);
            }
        }

        /// <summary>
        /// Removes several elements specified by an amount at a given index.
        /// </summary>
        public void RemoveAt(int index, int amount)
        {
            for (int i = 0; i < amount; i++)
            {
                RemoveAt(index);
            }
        } 

        /// <summary>
        /// Splices a list into n equal parts
        /// </summary>
        /// <returns>An enumerable collection of splices of the original list</returns>
        public static IEnumerable<SuperList<T>> operator /(SuperList<T> listToSplice, int n)
        {
            int divisions = listToSplice.Count/n;

            List<SuperList<T>> l = new List<SuperList<T>>();

            for (int i = 0; i < n; i++)
            {
                if (i == (n - 1))
                {
                    l.Add(listToSplice.Skip(i * divisions).ToSuperList());
                    continue;
                }
                l.Add(listToSplice.Skip(i*divisions).Take(divisions).ToSuperList());
            }

            return l;
        }
        
        /// <summary>
        /// Checks to see whether a collection of items is contained in the list
        /// </summary>
        /// <param name="inSequence">The list contains the elements found in the exact same order</param>
        public bool Contains(SuperList<T> items, bool inSequence)
        {
            if (!items.Any()) return false;
            if (inSequence)
            {
                int indexOf = IndexOf(items[0]);
                for (int i = indexOf; i < items.Count; i++)
                {
                    if(this.Count==i)break;
                    if (!((object) this[i]).Equals( items[i - indexOf]))
                        return false;
                }
                return true;
            }
            return items.All(i => this.Any(item=>((object) item).Equals(i)));
        }
    }

    public static class ListExtensions
    {
        /// <summary>
        /// Returns the IEnumerable collection as a super list object
        /// </summary>
        public static SuperList<T> ToSuperList<T>(this IEnumerable<T> k)
        {
            SuperList<T> result = k as SuperList<T>;
            
            if (result != null)
                return result;
            
            result = new SuperList<T>();
            k.ToList().ForEach(result.Add);
            return result;
        }
    }

}
